// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package signal

import (
	"context"
	"os"
	"sync"
)

var handlers struct {
	sync.Mutex
	// 将通道映射到应发送给它的信号。
	m map[chan<- os.Signal]*handler
	// 将信号映射到接收它的通道数。
	ref [numSig]int64
	// 在通道停止时将通道映射到信号。
	// 不是地图，因为这里的条目只存在很短的时间。
	// 我们需要一个单独的容器，因为我们需要m始终对应于ref 
	// 并且我们还需要跟踪正在停止的通道的*handler 
	// 值。请参阅停止功能。
	stopping []stopping
}

type stopping struct {
	c chan<- os.Signal
	h *handler
}

type handler struct {
	mask [(numSig + 31) / 32]uint32
}

func (h *handler) want(sig int) bool {
	return (h.mask[sig/32]>>uint(sig&31))&1 != 0
}

func (h *handler) set(sig int) {
	h.mask[sig/32] |= 1 << uint(sig&31)
}

func (h *handler) clear(sig int) {
	h.mask[sig/32] &^= 1 << uint(sig&31)
}

// 停止将信号SIG中继到之前注册到
// 的任何通道，接收信号并将信号处理程序重置为其原始值
// （操作=禁用信号）或忽略信号（操作=忽略信号）。
func cancel(sigs []os.Signal, action func(int)) {
	handlers.Lock()
	defer handlers.Unlock()

	remove := func(n int) {
		var zerohandler handler

		for c, h := range handlers.m {
			if h.want(n) {
				handlers.ref[n]--
				h.clear(n)
				if h.mask == zerohandler.mask {
					delete(handlers.m, c)
				}
			}
		}

		action(n)
	}

	if len(sigs) == 0 {
		for n := 0; n < numSig; n++ {
			remove(n)
		}
	} else {
		for _, s := range sigs {
			remove(signum(s))
		}
	}
}

// 忽略会导致忽略提供的信号。如果
// 程序收到它们，则不会发生任何事情。Ignore撤销之前任何
// 调用通知所提供信号的效果。
// 如果没有提供信号，所有输入信号将被忽略。
func Ignore(sig ...os.Signal) {
	cancel(sig, ignoreSignal)
}

// 忽略报告当前是否忽略sig。
func Ignored(sig os.Signal) bool {
	sn := signum(sig)
	return sn >= 0 && signalIgnored(sn)
}

var (
	// watchSignalLoopOnce guards调用有条件的
	// 初始化的watchSignalLoop。如果watchSignalLoop为非nil，
	// 一旦调用Notify，它将在goroutine中惰性地运行。
	// 见第21576期。
	watchSignalLoopOnce sync.Once
	watchSignalLoop     func()
)

// Notify使包信号将传入信号中继到c。
// 如果没有提供信号，则所有输入信号将中继到c。
// 否则，仅提供信号即可。
// 
// 包信号将不会阻止发送到c:调用方必须确保
// c有足够的缓冲空间来跟上预期的
// 信号速率。对于仅用于通知一个信号值的通道，
// 大小为1的缓冲区就足够了。
// 
// 允许在同一频道多次调用Notify:
// 每次调用都会扩展发送到该频道的信号集。
// 从集合中删除信号的唯一方法是调用Stop。
// 
// 允许使用不同通道多次调用Notify 
// 且信号相同：每个通道独立接收传入
// 信号的副本。
func Notify(c chan<- os.Signal, sig ...os.Signal) {
	if c == nil {
		panic("os/signal: Notify using nil channel")
	}

	handlers.Lock()
	defer handlers.Unlock()

	h := handlers.m[c]
	if h == nil {
		if handlers.m == nil {
			handlers.m = make(map[chan<- os.Signal]*handler)
		}
		h = new(handler)
		handlers.m[c] = h
	}

	add := func(n int) {
		if n < 0 {
			return
		}
		if !h.want(n) {
			h.set(n)
			if handlers.ref[n] == 0 {
				enableSignal(n)

				// 运行时要求我们在启动观察程序之前启用
				// 信号。
				watchSignalLoopOnce.Do(func() {
					if watchSignalLoop != nil {
						go watchSignalLoop()
					}
				})
			}
			handlers.ref[n]++
		}
	}

	if len(sig) == 0 {
		for n := 0; n < numSig; n++ {
			add(n)
		}
	} else {
		for _, s := range sig {
			add(signum(s))
		}
	}
}

// 重置将撤消之前为提供的
// 信号而进行的任何通知调用的效果。
// 如果没有提供信号，所有信号处理程序都将被重置。
func Reset(sig ...os.Signal) {
	cancel(sig, disableSignal)
}

// 停止导致包信号停止将传入信号中继到c。
// 使用c撤销之前所有通知调用的效果。
// 当Stop返回时，保证c不再接收信号。
func Stop(c chan<- os.Signal) {
	handlers.Lock()

	h := handlers.m[c]
	if h == nil {
		handlers.Unlock()
		return
	}
	delete(handlers.m, c)

	for n := 0; n < numSig; n++ {
		if h.want(n) {
			handlers.ref[n]--
			if handlers.ref[n] == 0 {
				disableSignal(n)
			}
		}
	}

	// 信号将不再传输到信道。
	// 我们希望避免对信号的竞争，例如SIGINT:
	// 它应该被传递到通道，或者程序应该采取默认操作（即退出）。
	// 为避免信号传递的可能性，
	// 调用信号处理程序，然后在下面的处理函数有机会
	// 通道，将该通道放在正在停止的
	// 在通道上发送信号之前，停止取消注册
	// 通道列表中，等待信号传递到
	// 停止，然后再完全移除它。

	handlers.stopping = append(handlers.stopping, stopping{c, h})

	handlers.Unlock()

	signalWaitUntilIdle()

	handlers.Lock()

	for i, s := range handlers.stopping {
		if s.c == c {
			handlers.stopping = append(handlers.stopping[:i], handlers.stopping[i+1:]...)
			break
		}
	}

	handlers.Unlock()
}

// 等待，直到不再有信号等待发送。
// 由运行时包定义。
func signalWaitUntilIdle()

func process(sig os.Signal) {
	n := signum(sig)
	if n < 0 {
		return
	}

	handlers.Lock()
	defer handlers.Unlock()

	for c, h := range handlers.m {
		if h.want(n) {
			// 发送，但不要为此阻止
			select {
			case c <- sig:
			default:
			}
		}
	}

	// 避免停止中提到的比赛。
	for _, d := range handlers.stopping {
		if d.h.want(n) {
			select {
			case d.c <- sig:
			default:
			}
		}
	}
}

// NotifyContext在下列信号之一到达、调用返回的stop函数或父上下文的
// （其done通道关闭）的父上下文副本，以先发生的为准。
// done通道关闭时，返回标记为done 
// 
// 停止函数取消注册信号行为，与signal.Reset一样，
// 可以恢复给定信号的默认行为。例如，默认的
// Go程序接收os.Interrupt的行为是退出。调用
// NotifyContext（父级，os.Interrupt）将更改行为以取消
// 返回的上下文。在调用返回的stop函数之前，接收到的未来中断不会触发默认
// /（退出）行为。
// 
// 停止函数释放与其相关的资源，因此代码应该在该上下文中运行的操作完成并且信号不再需要转移到上下文中时调用stop。
func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
	ctx, cancel := context.WithCancel(parent)
	c := &signalCtx{
		Context: ctx,
		cancel:  cancel,
		signals: signals,
	}
	c.ch = make(chan os.Signal, 1)
	Notify(c.ch, c.signals...)
	if ctx.Err() == nil {
		go func() {
			select {
			case <-c.ch:
				c.cancel()
			case <-c.Done():
			}
		}()
	}
	return c, c.stop
}

type signalCtx struct {
	context.Context

	cancel  context.CancelFunc
	signals []os.Signal
	ch      chan os.Signal
}

func (c *signalCtx) stop() {
	c.cancel()
	Stop(c.ch)
}

type stringer interface {
	String() string
}

func (c *signalCtx) String() string {
	var buf []byte
	// 我们知道c.Context的类型是Context.cancelCtx，我们知道cancelCtx的
	// String方法返回一个以“.WithCancel”结尾的字符串。
	name := c.Context.(stringer).String()
	name = name[:len(name)-len(".WithCancel")]
	buf = append(buf, "signal.NotifyContext("+name...)
	if len(c.signals) != 0 {
		buf = append(buf, ", ["...)
		for i, s := range c.signals {
			buf = append(buf, s.String()...)
			if i != len(c.signals)-1 {
				buf = append(buf, ' ')
			}
		}
		buf = append(buf, ']')
	}
	buf = append(buf, ')')
	return string(buf)
}
